#ifndef _CACHECMP_H
#define _CACHECMP_H

#include <iostream>
#include <fstream>
#include <sstream>
#include <memory>
#include <cassert>
#include <string.h>
#include <stdlib.h>
#include <zlib.h>
#include <math.h>
#include <vector>


#include "../../engine/address.hpp"
#include "../../utils/libcache/cache_types.h"
#include "../../utils/libcache/CacheWriteBack.hpp"

#include "CMP.h"

#include "constants.h"
#include "memory.h"
#include "trace.h"

class CacheCMP : public CMP {
    //CacheWriteBack <address_t>  *_L0Cache[128];
    CacheWriteBack <address_t> *_L1Cache[128];
    CacheWriteBack <address_t> *_L2Cache[128];
    uint32_t _chips,_threadsperchip;
    uint32_t _L1Size,_L2Size;
    bool _L1Prefetch;
    bool _L2Shared;
    uint32_t *_L1CacheHits, *_L1CacheHitInvalidate,*_L1CacheRemoteHits, *_L1CacheMisses,*_L1CacheReplacements;
    uint32_t *_L1CacheEvictions;
    uint32_t *_L2CacheHits, *_L2CacheHitInvalidate;
    uint32_t *_L2CacheRemoteHitsOnChip,*_L2CacheRemoteHitsOffChip;
    uint32_t *_L2CacheMisses,*_L2CacheReplacements;
    uint32_t *_L2CacheAccesses,*_L2CacheRemoteAccesses;
    uint32_t *_L2CacheWriteBacks;

    void reset();

    void updateL1(address_t lineAddress,uint32_t thread);
    void updateL2(address_t lineAddress,uint32_t thread);
    void dirtyL2(address_t lineAddress,uint32_t thread);
    int makeMemoryAccess(Memory *mem,uint32_t accessType,address_t lineAddress,uint32_t thread);
    int accessL2Shared(Memory *mem,uint32_t accessType,address_t lineAddress,uint32_t thread);
    int accessMultipleL2(uint32_t accessType,address_t lineAddress,uint32_t thread);
    void accessL0(address_t addr);
    bool accessL1(uint32_t accessType,address_t lineAddress,uint32_t thread);
    bool accessL2(uint32_t accessType,address_t lineAddress,uint32_t thread);
    void L1Prefetcher(uint32_t accessType,address_t addr,uint32_t thread);
    /* multi chip functions*/

    bool isLocalChipLocation(uint32_t location,uint32_t thread);
    bool accessRemoteL2(uint32_t accessType,address_t addr,uint32_t thread);



public:
    CacheCMP(uint32_t chips,uint32_t threadsperchip,
            uint32_t threads,uint32_t lineSize,uint32_t pageSize,
            uint32_t L0Size, uint32_t L1Size,
            uint32_t L2Size,bool L2Shared,
            bool L1Prefetch);
    ~CacheCMP();
    void executeCacheSimulation(Memory *mem,Trace *trace);
    void executeL0Simulation(Memory *mem,Trace *trace,uint32_t tid);

    inline bool isL2Shared()                          const { return _L2Shared; }
    inline uint32_t getL1Size()                       const { return _L1Size;   }
    inline uint32_t getL2Size()                       const { return _L2Size;   }
    inline uint32_t getL1CacheHits(int tid)           const { return _L1CacheHits[tid]; }
    inline uint32_t getL1CacheHitInvalidate(int tid)  const { return _L1CacheHitInvalidate[tid]; }
    inline uint32_t getL1CacheRemoteHits(int tid)     const { return _L1CacheRemoteHits[tid]; }
    inline uint32_t getL1CacheMisses(int tid)         const { return _L1CacheMisses[tid]; }
    inline uint32_t getL1CacheReplacements(int tid)   const { return _L1CacheReplacements[tid]; }
    inline uint32_t getL1CacheEvictions(int tid)      const { return _L1CacheEvictions[tid]; }
    inline uint32_t getL2CacheHits(int tid)           const { return _L2CacheHits[tid]; }
    inline uint32_t getL2CacheHitInvalidate(int tid)  const { return _L2CacheHitInvalidate[tid]; }
    inline uint32_t getL2CacheAccesses(int tid)       const { return _L2CacheAccesses[tid]; }
    inline uint32_t getL2CacheRemoteAccesses(int tid) const { return _L2CacheRemoteAccesses[tid]; }
    inline uint32_t getL2CacheRemoteHitsOnChip(int tid)  const { return _L2CacheRemoteHitsOnChip[tid]; }
    inline uint32_t getL2CacheRemoteHitsOffChip(int tid) const { return _L2CacheRemoteHitsOffChip[tid]; }
    inline uint32_t getL2CacheMisses(int tid)         const { return _L2CacheMisses[tid]; }
    inline uint32_t getL2CacheReplacements(int tid)   const { return _L2CacheReplacements[tid]; }
    inline uint32_t getL2CacheWriteBacks(int tid)     const { return _L2CacheWriteBacks[tid]; }
};

#endif
